﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using NasuTek.Shion.Agent;
using System.Threading;
using System.IO;
using Skybound.Gecko;
using System.Xml.Serialization;
using System.Xml;
using System.Diagnostics;
using NasuTek.Shion.Extencibility;
using System.Resources;
using System.Text.RegularExpressions;

namespace NasuTek.Shion
{
    public partial class Form1
    {
        public void WriteToChannelText(string text, string channel, string server)
        {
            if (Clients.ContainsKey(server))
            {
                List<string> i = new List<string>(ChannelText[server][channel.ToLower()]);
                if (i.Count > 100) { i.RemoveAt(0); }
                i.Add(text);
                ChannelText[server][channel.ToLower()] = i.ToArray();
                if ((em.activeChannel == channel.ToLower()) && (em.activeServer == server)) RefreshWindow();
            }
        }

        public void V()
        {
            try
            {
                foreach (KeyValuePair<string, IrcClient> i in Clients)
                {
                    try
                    {
                        i.Value.RfcQuit();
                    }
                    catch { }
                }
            }
            catch { V(); }
        }

        public void GetConfig()
        {
            if (Program.debug == false)
            {
                if (File.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Shion6.xml")))
                {
                    XmlSerializer x = new XmlSerializer(typeof(Configuration));
                    FileStream fs = new FileStream(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Shion6.xml"), FileMode.Open);
                    config = (Configuration)x.Deserialize(XmlReader.Create(fs));
                    fs.Close();
                }
                else { config = new Configuration(); }
            }
            else
            {
                if (File.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Shion6-debug.xml")))
                {
                    XmlSerializer x = new XmlSerializer(typeof(Configuration));
                    FileStream fs = new FileStream(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Shion6-debug.xml"), FileMode.Open);
                    config = (Configuration)x.Deserialize(XmlReader.Create(fs));
                    fs.Close();
                }
                else { config = new Configuration(); }
            }
        }

        static public void SaveConfig()
        {
            if (Program.debug == false)
            {
                XmlSerializer x = new XmlSerializer(typeof(Configuration));
                FileStream fs = File.Create(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Shion6.xml"));
                x.Serialize(XmlWriter.Create(fs), config);
                fs.Close();
            }
            else
            {
                XmlSerializer x = new XmlSerializer(typeof(Configuration));
                FileStream fs = File.Create(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Shion6-debug.xml"));
                x.Serialize(XmlWriter.Create(fs), config);
                fs.Close();
            }
        }

        public void ModeOperation(string newmode, string nick, string channel, string server)
        {
            if (newmode == "-o") { Ops[server][channel.ToLower()].Remove(nick); }
            else if (newmode == "-h") { HalfOps[server][channel.ToLower()].Remove(nick); }
            else if (newmode == "-v") { Vops[server][channel.ToLower()].Remove(nick); }
            else if (newmode == "+o") { Ops[server][channel.ToLower()].Add(nick); }
            else if (newmode == "+h") { HalfOps[server][channel.ToLower()].Add(nick); }
            else if (newmode == "+v") { Vops[server][channel.ToLower()].Add(nick); }
            if ((em.activeServer == server) && (em.activeChannel == channel))
            {
                Invoke(new Action(delegate { NickList.Items[nick].Group = NickList.Groups[GetHighestMode(nick, server, channel)]; }));
            }
        }

        public void CreateServer(string ircaddress, string nick, string username, string realname, string NickServPassword, string OperUserName, string OperPassword, bool add)
        {
            try
            {
                Clients.Add(ircaddress, new IrcClient());
                Clients[ircaddress].CtcpVersion = "Shion 6 Release Canadate 1 Version " + GetType().Assembly.GetName().Version.ToString() + " (Shion Agent Version " + Clients[ircaddress].VersionNumber + ")";
                Clients[ircaddress].Connect(ircaddress, 6667);
                ChannelText.Add(ircaddress, new Dictionary<string, string[]>());
                NamesList.Add(ircaddress, new Dictionary<string, string[]>());
                Ops.Add(ircaddress, new Dictionary<string, List<string>>());
                HalfOps.Add(ircaddress, new Dictionary<string, List<string>>());
                Vops.Add(ircaddress, new Dictionary<string, List<string>>());
                Topics.Add(ircaddress, new Dictionary<string, string>());
                ChannelModes.Add(ircaddress, new Dictionary<string, string>());
                em.HostMasks.Add(ircaddress, new List<string>());

                Clients[ircaddress].SupportNonRfc = true;
                Clients[ircaddress].OnQuit += new QuitEventHandler(Form1_OnQuit);
                Clients[ircaddress].OnTopic += new TopicEventHandler(Form1_OnTopic);
                Clients[ircaddress].OnOp += new OpEventHandler(Form1_OnOp);
                Clients[ircaddress].OnDeop += new DeopEventHandler(Form1_OnDeop);
                Clients[ircaddress].OnHalfop += new HalfopEventHandler(Form1_OnHalfop);
                Clients[ircaddress].OnDehalfop += new DehalfopEventHandler(Form1_OnDehalfop);
                Clients[ircaddress].OnVoice += new VoiceEventHandler(Form1_OnVoice);
                Clients[ircaddress].OnDevoice += new DevoiceEventHandler(Form1_OnDevoice);
                Clients[ircaddress].OnChannelMessage += new IrcEventHandler(Form1_OnChannelMessage);
                Clients[ircaddress].OnChannelAction += new ActionEventHandler(Form1_OnChannelAction);
                Clients[ircaddress].OnChannelNotice += new IrcEventHandler(Form1_OnChannelNotice);
                Clients[ircaddress].OnQueryNotice += new IrcEventHandler(Form1_OnQueryNotice);
                Clients[ircaddress].OnQueryMessage += new IrcEventHandler(Form1_OnQueryMessage);
                Clients[ircaddress].OnTopicChange += new TopicChangeEventHandler(Form1_OnTopicChange);
                Clients[ircaddress].OnNickChange += new NickChangeEventHandler(Form1_OnNickChange);
                Clients[ircaddress].OnJoin += new JoinEventHandler(Form1_OnJoin);
                Clients[ircaddress].OnNames += new NamesEventHandler(Form1_OnNames);
                Clients[ircaddress].OnKick += new KickEventHandler(Form1_OnKick);
                Clients[ircaddress].OnBan += new BanEventHandler(Form1_OnBan);
                Clients[ircaddress].OnUnban += new UnbanEventHandler(Form1_OnUnban);
                Clients[ircaddress].OnPart += new PartEventHandler(Form1_OnPart);
                Clients[ircaddress].OnMotd += new MotdEventHandler(Form1_OnMotd);
                Clients[ircaddress].OnWriteLine += new WriteLineEventHandler(Form1_OnWriteLine);
                Clients[ircaddress].OnRawMessage += new IrcEventHandler(Form1_OnRawMessage);
                Clients[ircaddress].OnError += new NasuTek.Shion.Agent.ErrorEventHandler(Form1_OnError);
                Clients[ircaddress].DisconnectedHandle += new DisconnectedEventHandler(Form1_DisconnectedHandle);
                Clients[ircaddress].OnModeChange += new IrcEventHandler(Form1_OnModeChange);
                Clients[ircaddress].OnWho += new WhoEventHandler(Form1_OnWho);
                Thread newThread = new Thread(new ThreadStart(Clients[ircaddress].Listen));
                Clients[ircaddress].Login(nick, realname, 0, username);
                ServerExplorer.Nodes.Add(ircaddress, ircaddress);
                ServerExplorer.Nodes[ircaddress].Nodes.Add("Console", "Console", "Console", "Console");
                ChannelText[ircaddress].Add("console", new string[] { "echo►Connecting to server " + ircaddress + "►" + DateTime.Now.ToShortTimeString() });
                SetActiveWindow(ircaddress, "Console");
                if (add == true) { config.Servers.Add(new Server(ircaddress, nick, realname, username, NickServPassword, OperUserName, OperPassword)); SaveConfig(); }
                newThread.Start();
                if ((NickServPassword != null) && (NickServPassword != ""))
                {
                    Clients[ircaddress].SendMessage(SendType.Message, "NickServ", "identify " + NickServPassword);
                }
                if ((OperPassword != null) && (OperPassword != ""))
                {
                    Clients[ircaddress].WriteLine("OPER " + OperUserName + " " + OperPassword);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
                Clients.Remove(ircaddress);
            }
        }

        private void CleanNamesList(string server, List<string> information)
        {
            try
            {
                foreach (KeyValuePair<string, string[]> v in NamesList[server])
                {
                    if (!information.Contains(v.Key))
                    {
                        List<string> i = new List<string>(v.Value);
                        i.RemoveAll(Blank);
                        NamesList[server][v.Key] = i.ToArray();
                        information.Add(v.Key);
                    }
                }
            }
            catch { CleanNamesList(server, information); }
        }

        private static bool Blank(String s)
        {
            if ((s == "") || (s == null))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public void JoinChannel(string ircaddress, string channel, bool add)
        {
            try
            {
                Clients[ircaddress].RfcJoin(channel);
                if (add == true)
                {
                    config.GetServer(ircaddress).AddChannel(channel);
                    SaveConfig();
                }
            }
            catch (KeyNotFoundException) { }
        }

        public void SetActiveWindow(string ircaddress, string channel) { Invoke(new SetActiveWindowDelegate(SetActiveWindow), new object[] { ircaddress, channel, true }); }
        public delegate void SetActiveWindowDelegate(string ircaddress, string channel, bool Invoking);
        public void SetActiveWindow(string ircaddress, string channel, bool Invoking)
        {
            em.activeServer = ircaddress;
            em.activeChannel = channel.ToLower();
            Text = String.Format("NasuTek Shion 6 Release Canadate 1 - {0} - {1}", em.activeServer, em.activeChannel);
            if ((em.activeChannel != "console") && (em.activeChannel.Contains('#')) && (em.activeChannel != "debug"))
            {
                RefreshNames();
            }
            else { toolStripStatusLabel2.Text = ""; NickList.Items.Clear(); }
            if ((em.activeChannel != "") && (em.activeServer != "")) RefreshWindow();
        }

        public void RefreshNames() { Invoke(new Action<bool>(RefreshNames), new object[] { true }); }
        public void RefreshNames(bool Invoking)
        {
            List<string> addedNames = new List<string>();
            toolStripStatusLabel2.Text = String.Format("{0} Users", NamesList[em.activeServer][em.activeChannel].Length);
            NickList.Items.Clear();
            List<ListViewItem> items = new List<ListViewItem>();
            foreach (string i in Ops[em.activeServer][em.activeChannel])
            {
                if (addedNames.Contains(i) != true)
                {
                    ListViewItem x = new ListViewItem(i, NickList.Groups["Operators"]);
                    x.Name = i;
                    items.Add(x);
                    addedNames.Add(i);
                }
            }
            foreach (string i in HalfOps[em.activeServer][em.activeChannel])
            {
                if (addedNames.Contains(i) != true)
                {
                    ListViewItem x = new ListViewItem(i, NickList.Groups["Half-Operators"]);
                    x.Name = i;
                    items.Add(x);
                    addedNames.Add(i);
                }
            }
            foreach (string i in Vops[em.activeServer][em.activeChannel])
            {
                if (addedNames.Contains(i) != true)
                {
                    ListViewItem x = new ListViewItem(i, NickList.Groups["Voice"]);
                    x.Name = i;
                    items.Add(x);
                    addedNames.Add(i);
                }
            }
            foreach (string i in NamesList[em.activeServer][em.activeChannel])
            {
                if (addedNames.Contains(i) != true)
                {
                    ListViewItem x = new ListViewItem(i, NickList.Groups["Users"]);
                    x.Name = i;
                    items.Add(x);
                    addedNames.Add(i);
                }
            }
            NickList.Items.AddRange(items.ToArray());
        }

        public void RefreshWindow() { Invoke(new Action<bool>(RefreshWindow), new object[] { true }); }
        public void RefreshWindow(bool Invoking)
        {
            if (!exiting)
            {
                if ((em.activeChannel != "console") && (em.activeChannel.Contains('#'))) { Topic.Text = Topics[em.activeServer][em.activeChannel]; }
                else { Topic.Text = ""; }
                List<string> HTML = new List<string>();
                Theme theme = em.GetTheme(config.Theme);

                HTML.Add("<html>");
                HTML.Add("<head>");
                HTML.Add("<style type=\"text/css\">");
                HTML.Add("span.color0 { color: white; }");
                HTML.Add("span.color1 { color: black; }");
                HTML.Add("span.color2 { color: blue; }");
                HTML.Add("span.color3 { color: green; }");
                HTML.Add("span.color4 { color: red; }");
                HTML.Add("span.color5 { color: darkred; }");
                HTML.Add("span.color6 { color: purple; }");
                HTML.Add("span.color7 { color: orange; }");
                HTML.Add("span.color8 { color: yellow; }");
                HTML.Add("span.color9 { color: lightgreen; }");
                HTML.Add("span.color10 { color: teal; }");
                HTML.Add("span.color11 { color: aqua; }");
                HTML.Add("span.color12 { color: darkblue; }");
                HTML.Add("span.color13 { color: pink; }");
                HTML.Add("span.color14 { color: gray; }");
                HTML.Add("span.color15 { color: lightgray; }");
                HTML.Add("span.background0 { background-color: white; }");
                HTML.Add("span.background1 { background-color: black; }");
                HTML.Add("span.background2 { background-color: blue; }");
                HTML.Add("span.background3 { background-color: green; }");
                HTML.Add("span.background4 { background-color: red; }");
                HTML.Add("span.background5 { background-color: darkred; }");
                HTML.Add("span.background6 { background-color: purple; }");
                HTML.Add("span.background7 { background-color: orange; }");
                HTML.Add("span.background8 { background-color: yellow; }");
                HTML.Add("span.background9 { background-color: lightgreen; }");
                HTML.Add("span.background10 { background-color: teal; }");
                HTML.Add("span.background11 { background-color: aqua; }");
                HTML.Add("span.background12 { background-color: darkblue; }");
                HTML.Add("span.background13 { background-color: pink; }");
                HTML.Add("span.background14 { background-color: gray; }");
                HTML.Add("span.background15 { background-color: lightgray; }");
                foreach (string i in theme.GetStyleSheet())
                {
                    HTML.Add(i);
                }
                HTML.Add("</style>");
                HTML.Add("</head>");
                HTML.Add("<body>");
                if (em.activeChannel != "debug")
                {
                    foreach (string i in ChannelText[em.activeServer][em.activeChannel])
                    {
                        string xdm = LineParser.ParseToSpan(i);
                        string[] data = xdm.Split('►');
                        if ((i.Contains(Clients[em.activeServer].Nickname)) &&
                            (data[0].Contains("own") != true) && (data[0] != "join") &&
                            (data[0] != "modechange") && (data[0] != "kicked") &&
                            (data[0] != "banned") &&
                            (data[0] != "unbanned"))
                            HTML.Add("<div class=\"highlight\"><div class=\"" + data[0] + "\">" + theme.ParseData(data) + "</div></div>");

                        else HTML.Add("<div class=\"" + data[0] + "\">" + theme.ParseData(data) + "</div>");
                    }
                }
                else
                {
                    foreach (string i in Raw)
                    {
                        string xdm = LineParser.ParseToSpan(i);
                        string[] data = xdm.Split('►');
                        HTML.Add("<div class=\"" + data[0] + "\">" + theme.ParseData(data) + "</div>");
                    }
                }
                HTML.Add("<a name=\"END\"></a>");
                HTML.Add("</body>");
                HTML.Add("</html>");
                HTML.Add("<script language=\"javascript\">");
                HTML.Add("document.location.href=\"#END\";");
                HTML.Add("</script>");
                if (Program.debug == false)
                {
                    File.WriteAllLines(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "ShionTemp\\ShionTempHTML.html"), HTML.ToArray());
                    webBrowser1.Navigate(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "ShionTemp\\ShionTempHTML.html"));
                }
                else
                {
                    File.WriteAllLines(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "ShionTemp-debug\\ShionTempHTML.html"), HTML.ToArray());
                    webBrowser1.Navigate(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "ShionTemp-debug\\ShionTempHTML.html"));
                }
            }
        }

        public void ClearAll(List<string> clearList)
        {
            try
            {
                foreach (KeyValuePair<string, string[]> i in ChannelText[em.activeServer])
                {
                    if (clearList.Contains(i.Key) == false)
                    {
                        clearList.Add(i.Key);
                        ChannelText[em.activeServer][i.Key] = new string[] { };
                    }
                }
            }
            catch (InvalidOperationException)
            {
                ClearAll(clearList);
            }
        }

        public string GetHighestMode(string nick, string server, string channel)
        {
            string mode = "Users";
            if (Form1.Vops[server][channel.ToLower()].Contains(nick)) mode = "Voice";
            if (Form1.HalfOps[server][channel.ToLower()].Contains(nick)) mode = "Half-Operators";
            if (Form1.Ops[server][channel.ToLower()].Contains(nick)) mode = "Operators";
            return mode;
        }
    }
}